package com.gitee.kenewstar.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.gitee.kenewstar.exception.CommonException;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.poi.util.StringUtil;

import java.util.*;

/**
 * <p>
 *     jwt工具类
 * </p>
 * <p>
 *     数据格式：header.payload.signature
 * </p>
 * <p>
 *     Header
 *     JWT 的 header 中承载了两部分信息
 *     {
 *   	 "alg": "RS256", 声明加密算法
 *   	 "typ": "JWT"   声明类型
 * 	   }
 * 	   对这个头部信息进行 base64，即可得到 header 部分
 * </p>
 * <p>
 *     Payload
 *     payload 是主体部分，意为载体，承载着有效的 JWT 数据包，它包含三个部分
 *
 *     标准声明
 *     公共声明
 *     私有声明
 *     上面的 JWT 的 payload 结构是这样的
 *
 * 	   {
 *       "ip": "127.0.0.1",
 *       "uuid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
 *       "iat": 1527523017
 *     }
 * </p>
 * <p>
 *     Signature
 *     signature 是签证信息，该签证信息是通过header和payload，加上secret，通过算法加密生成。
 *     公式 signature = 加密算法(header + "." + payload, 密钥);
 *     上面的 header 中，我们已经定义了加密算法使用 RS256，也已经实现了生成header和payload，下面我们来生成 signature
 * </p>
 * <p>
 * 安全性相关
 * 如果加强 JWT 的安全性？
 *
 * 根据我的使用，总结以下几点：
 *
 * 缩短 token 有效时间
 * 使用安全系数高的加密算法
 * token 不要放在 Cookie 中，有 CSRF 风险
 * 使用 HTTPS 加密协议
 * 对标准字段 iss、sub、aud、nbf、exp 进行校验
 * 使用成熟的开源库，不要手贱造轮子
 * 特殊场景下可以把用户的 UA、IP 放进 payload 进行校验(不推荐)
 * </p>
 * @author kenewstar
 * @date 2021/7/18
 */
public abstract class JwtUtil {

	public static final String SECRET = "KeNewStar";
	public static final String USER_FLAG = "username";

	/**
	 * 生成token
	 * @param username 用户名
	 * @return token
	 */
	public static String generateToken(String username) {
		Date iatDate = new Date();
		// expire time
		Calendar nowTime = Calendar.getInstance();
		nowTime.add(Calendar.DATE, 10);
		Date expireTime = nowTime.getTime();
		// header map
		Map<String, Object> headerMap = new HashMap<>(2);
		headerMap.put("alg", "RS256");
		headerMap.put("typ", "JWT");

		// create token
		String token = JWT.create()
				.withHeader(headerMap)
				.withClaim("iss", "Service")
				.withClaim("aud", "APP")
				.withClaim(USER_FLAG, username)
				.withIssuedAt(iatDate)
				.withExpiresAt(expireTime)
				.sign(Algorithm.HMAC256(SECRET));
		// 返回token
		return token;
	}

	/**
	 * 解密token
	 * @param token token
	 * @return 解密信息
	 */
	public static Map<String, Claim> verifyToken(String token) {
		DecodedJWT jwt;
		try {
			JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
			jwt = jwtVerifier.verify(token);
		} catch (Exception e) {
			throw new CommonException("token error", e);
		}
		return jwt.getClaims();
	}

	/**
	 * 根据token获取userFlag
	 * @param token token
	 * @return 用户标识
	 */
	public static String getUsernameByToken(String token) {
		Map<String, Claim> claimMap = verifyToken(token);
		Claim userClaim = claimMap.get(USER_FLAG);
		if (Objects.isNull(userClaim) || Objects.equals("", userClaim.asString())) {
			throw new CommonException("token error");
		}
		return userClaim.asString();
	}


}
